package com.piper.config;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Map;

@WebFilter(filterName = "", urlPatterns = {"/**",""})
public class HttpClientFilter implements Filter {

    private WebApplicationContext wac;

    private ConnectionManager manager;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        wac = ContextLoader.getCurrentWebApplicationContext();
        manager = wac.getBean(ConnectionManager.class);
        System.out.println("init filter");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse;

        //~1
        //将请求转到对应的服务上
        // 比如requestUri：localhost:8080/eureka 转到 172.30.10.73:8762
        // 比如requestUri：localhost:8080/eureka/min.js 转到 172.30.10.73:8762/min.js
        // 拼接后的地址在 uris 变量上

        String uri = httpServletRequest.getRequestURI();
        System.out.println(uri);
        Map<String, String> urlMap = UrlProperties.map;
        String path = "";
        try {
            int lastIndex = uri.indexOf("/", 1);
            if (lastIndex == -1) {
                path = uri.substring(1);
            } else {
                path = uri.substring(1, uri.indexOf("/", 1));
            }
        } catch (StringIndexOutOfBoundsException e) {
            e.printStackTrace();
        }
        String uris = "";
        if (urlMap.containsKey(path)) {
            uris = urlMap.get(path) + uri.substring(path.length() + 1);
        } else {

            //转发到自己的工程

        }
        //~1

        CloseableHttpClient httpclient = manager.getHttpClient();
        CloseableHttpResponse response = null;
        try {
            //~2
            // 判断前台的请求方式 对应创建不同类型请求 因为post和get传值方式不同
            String reqMethod = httpServletRequest.getMethod();
            System.out.println("什么请求方式：" + reqMethod);
            if ("POST".equals(reqMethod)) {
                HttpPost httpPost = new HttpPost(uris);
//                httpPost.releaseConnection();


                //将前台的请求体放到我的请求体里
                httpPost.setEntity(new InputStreamEntity(httpServletRequest.getInputStream()));

//                httpPost.setHeaders(doHeaders(httpServletRequest));  //post如果有参数 不需要带头部

                response = httpclient.execute(httpPost);
            }
            if ("GET".equals(reqMethod)) {
                URIBuilder builder = new URIBuilder(uris);
                Enumeration<String> enumeration = httpServletRequest.getParameterNames();
                //将前台的参数放到我的请求里
                while (enumeration.hasMoreElements()) {
                    String next = enumeration.nextElement();
                    builder.setParameter(next, httpServletRequest.getParameter(next));
                }
                HttpGet httpGet = new HttpGet(builder.build());

//                httpGet.setHeaders(doHeaders(httpServletRequest));

                response = httpclient.execute(httpGet);
            }
            //~2

            //~3
            // html类型数据需要在里面的css和js链接前面加入标记，其他的数据直接返回前台
            // 目前只测试了html，js，css，png  其他返回类型还要测试
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                //处理响应
                doResponse(httpServletResponse, response);

                //处理html
                if (response.getFirstHeader("Content-Type").getValue().contains("text/html")) {
                    htmlHandle(httpServletResponse, response, path);
                } else {
                    //处理其他
                    ServletOutputStream out = httpServletResponse.getOutputStream();
                    HttpEntity entity = response.getEntity();
                    fileHandle(entity, out);
                }
            } else {
                httpServletResponse.sendError(response.getStatusLine().getStatusCode());
            }
            //~3
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
//                httpclient.close();   //关了会报连接池关闭异常
                if (null != response) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 处理响应
     *
     * @param servletRes
     * @param proxyRes
     */
    public void doResponse(HttpServletResponse servletRes, CloseableHttpResponse proxyRes) {
        // 处理消息头
        Header[] headers = proxyRes.getAllHeaders();
        if (headers != null) {
            System.out.println("相应头");
            for (Header header : headers) {
                System.out.println(header.getName() + " : " + header.getValue());
                servletRes.addHeader(header.getName(), header.getValue());
            }
        }
    }

    //处理html
    public void htmlHandle(HttpServletResponse httpServletResponse, CloseableHttpResponse response, String path) {
        ServletOutputStream out = null;
        try {
            String html = EntityUtils.toString(response.getEntity());
            String res = htmlConvert(html, path);

            PrintWriter writer = httpServletResponse.getWriter();
            writer.print(res);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != response) {
                    response.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //非html直接返回
    public void fileHandle(HttpEntity entity, ServletOutputStream out) {
        try {
            entity.writeTo(out);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 处理html数据里的 js，css，png等的链接地址
     * 方便后面进行转发
     *
     * @param html
     * @return
     */
    String htmlConvert(String html, String path) {

//        html = html.replace("ui-sref=\"{{view.state}}\"","ui-sref=\"{{view.state.substring(2,view.state.length)}}\"");

        StringBuffer str = new StringBuffer(html);

        //path形如： kafka,eureka之类，是yml文件里map属性下的服务名
        path = "/" + path;
        String path2 = path + "/";


        Document doc = Jsoup.parse(html);

        Elements a = doc.select("[href~=#]");
        for (Element e : a) {
            e.removeAttr("href");
        }


        // 找到所有包含src属性，但是链接不以http开头的元素
        Elements src = doc.select("[src~=^(?!http).*$]");
        Elements href = doc.select("[href~=^(?!(http|#)).*$]");
        Elements action = doc.select("[action]");

        for (Element e : src) {
            String link = e.attr("src");
            if (link.startsWith("/")) {
                e.attr("src", path + link);
            } else {
                e.attr("src", path2 + link);
            }
        }
        for (Element e : href) {
            String link = e.attr("href");
            if (link.startsWith("/")) {
                e.attr("href", path + link);
            } else {
                e.attr("href", path2 + link);
            }
        }
        for (Element e : action) {
            String link = e.attr("action");
            if (link.startsWith("/")) {
                e.attr("action", path + link);
            } else {
                e.attr("action", path2 + link);
            }
        }

        return doc.outerHtml();
    }

    @Override
    public void destroy() {
        System.out.println("destory filter");
    }
}
